#include "BoxGeometry.h"
POCELAIN_NAMESPACE_BEGIN

BoxGeometry::BoxGeometry(Real width,Real height,Real depth,size_t widthSegments,size_t heightSegments,size_t depthSegments)
{
    // segments
    widthSegments = std::floor( widthSegments );
    heightSegments = std::floor( heightSegments );
    depthSegments = std::floor( depthSegments );

    // buffers
    BufferGeometry::index_array_t::array_t indices;
    Float32BufferAttribute::array_t vertices;
    Float32BufferAttribute::array_t normals;
    Float32BufferAttribute::array_t uvs;

    size_t numberOfVertices = 0;
    size_t groupStart = 0;

    const size_t x = 0;
    const size_t y = 1;
    const size_t z = 2;
    // build each side of the box geometry
    buildPlane( z, y, x, - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ,indices , vertices , normals , uvs , numberOfVertices, groupStart ); // px
    buildPlane( z, y, x, 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ,indices , vertices , normals , uvs , numberOfVertices, groupStart ); // nx
    buildPlane( x, z, y, 1, 1, width, depth, height, widthSegments, depthSegments, 2 ,indices , vertices , normals , uvs , numberOfVertices, groupStart ); // py
    buildPlane( x, z, y, 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ,indices , vertices , normals , uvs , numberOfVertices, groupStart ); // ny
    buildPlane( x, y, z, 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ,indices , vertices , normals , uvs , numberOfVertices, groupStart ); // pz
    buildPlane( x, y, z, - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ,indices , vertices , normals , uvs , numberOfVertices, groupStart ); // nz

    auto indicesAtt = new index_array_t(indices,1);
    auto verticesAtt = new Float32BufferAttribute( vertices, 3 );
    auto normalsAtt = new Float32BufferAttribute( normals, 3 );
    auto uvsAtt = new Float32BufferAttribute( uvs, 2 );

    // build geometry
    this->setIndices( indicesAtt );
    this->setAttribute( "position", verticesAtt );
    this->setAttribute( "normal", normalsAtt );
    this->setAttribute( "uv", uvsAtt);

    indicesAtt->release();
    verticesAtt->release();
    normalsAtt->release();
    uvsAtt->release();

}

void BoxGeometry::buildPlane(
    size_t u,
    size_t v,
    size_t w,
    int udir,
    int vdir,
    Real width,
    Real height,
    Real depth,
    size_t gridX,
    size_t gridY,
    size_t materialIndex,
    index_array_t::array_t &indices,
    Float32BufferAttribute::array_t &vertices,
    Float32BufferAttribute::array_t &normals,
    Float32BufferAttribute::array_t &uvs,
    size_t &numberOfVertices,
    size_t &groupStart
    )
{

    const auto segmentWidth = width / gridX;
    const auto segmentHeight = height / gridY;

    const auto widthHalf = width / 2;
    const auto heightHalf = height / 2;
    const auto depthHalf = depth / 2;

    const auto gridX1 = gridX + 1;
    const auto gridY1 = gridY + 1;

    size_t vertexCounter = 0;
    size_t groupCount = 0;

    Vector3 vector;

    // generate vertices, normals and uvs

    for ( size_t iy = 0; iy < gridY1;  ++iy )
    {
        const auto y = iy * segmentHeight - heightHalf;
        for ( size_t ix = 0; ix < gridX1; ++ix  )
        {
            const auto x = ix * segmentWidth - widthHalf;

            // set values to correct vector component
            vector[ u ] = x * udir;
            vector[ v ] = y * vdir;
            vector[ w ] = depthHalf;

            // now apply vector to vertex buffer
            vertices.push_back( vector.x);
            vertices.push_back( vector.y);
            vertices.push_back( vector.z);

            // set values to correct vector component
            vector[ u ] = 0;
            vector[ v ] = 0;
            vector[ w ] = depth > 0 ? 1 : - 1;

            // now apply vector to normal buffer
            normals.push_back( vector.x);
            normals.push_back( vector.y);
            normals.push_back( vector.z);

            // uvs
            uvs.push_back( ix / gridX );
            uvs.push_back( 1 - ( iy / gridY ) );

            // counters
            vertexCounter += 1;
        }
    }

    // indices

    // 1. you need three indices to draw a single face
    // 2. a single segment consists of two faces
    // 3. so we need to generate six (2*3) indices per segment

    for ( size_t iy = 0; iy < gridY; ++iy )
    {
        for ( size_t ix = 0; ix < gridX; ++ix )
        {
            const auto a = numberOfVertices + ix + gridX1 * iy;
            const auto b = numberOfVertices + ix + gridX1 * ( iy + 1 );
            const auto c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 );
            const auto d = numberOfVertices + ( ix + 1 ) + gridX1 * iy;

            // faces
            indices.push_back( a );
            indices.push_back( b );
            indices.push_back( d );
            indices.push_back( b );
            indices.push_back( c );
            indices.push_back( d );

            // increase counter
            groupCount += 6;
        }
    }

    // add a group to the geometry. this will ensure multi material support
    addGroup( groupStart, groupCount, materialIndex );

    // calculate new start value for groups
    groupStart += groupCount;

    // update total number of vertices
    numberOfVertices += vertexCounter;

}

POCELAIN_NAMESPACE_END
